class Layer{
  
 private Integrator[] upper_integrator;
 private Integrator[] lower_integrator;
 
 private float[] data;
 private float[] upper_offset;
 private float[] lower_offset;
 private float[] offset; // offset from previous data points
 
 private ColorIntegrator color_integrator; 
 private color layerColor;
 
 private ColorIntegrator stroke_color_integrator; 
 private color strokeColor;
 
  private ColorIntegrator highlight_color_integrator; 
 private color highlightColor;

 private float totalWeight;
 private float sd;
 
 private int numData;
 private color default_color = 0x00000000;
 
 private int id;
  
 // Constructors
 Layer(int _id, int _numData){
   id = _id;
   initVar(_numData);
   color_integrator = new ColorIntegrator(default_color, 30);
   layerColor = default_color;
   
   highlight_color_integrator = new ColorIntegrator(0xFFFFFFFF, 30);
   highlightColor = 0xFFFFFFFF;
   
   stroke_color_integrator = new ColorIntegrator(default_color, 5);
   strokeColor = default_color;
   
 }


  Layer(int _id, int _numData, color _color){
   id = _id;
   initVar(_numData);
   color_integrator = new ColorIntegrator(_color, 30);
   stroke_color_integrator = new ColorIntegrator(_color, 30);
   highlight_color_integrator = new ColorIntegrator(_color, 5);
   layerColor = _color;
   strokeColor = _color;
   highlightColor = _color;
 } 
 
 Layer(int _id, int _numData, color _color, boolean init_integrator){
   id = _id;
   initVar(_numData, init_integrator);
   color_integrator = new ColorIntegrator(_color, 30);
   stroke_color_integrator = new ColorIntegrator(_color, 30);
   highlight_color_integrator = new ColorIntegrator(_color, 5);
   layerColor = _color;
   strokeColor = _color;
   highlightColor = _color;
 } 
 
 Layer(int _id, int _numData, color _color, color _strokeColor){
   id= _id;
   initVar(_numData);
   color_integrator = new ColorIntegrator(_color, 30);
   stroke_color_integrator = new ColorIntegrator(_strokeColor, 30);
   highlight_color_integrator = new ColorIntegrator(_strokeColor, 5);
   layerColor = _color;
   strokeColor = _strokeColor;
   highlightColor = _strokeColor;
 } 
 
 // Initialize data to a starting value
 void initVar(int _numData){
   numData = _numData;
   data = new float[numData];
   upper_offset = new float[numData];
   lower_offset = new float[numData];
   offset = new float[numData];
   upper_integrator = new Integrator[numData];
   lower_integrator = new Integrator[numData];
   initializeData(0);
 }
 
 // Initialize data to a starting value
 void initVar(int _numData, boolean initIntegrator){
   numData = _numData;
   data = new float[numData];
   upper_offset = new float[numData];
   lower_offset = new float[numData];
   offset = new float[numData];
   upper_integrator = new Integrator[numData];
   lower_integrator = new Integrator[numData];
   
   if(initIntegrator) initializeData(0);
   else{
     for (int i =0; i < numData; i++){
        data[i] = 0;
        upper_offset[i] = 0;
        lower_offset[i] = 0;
        offset[i] = 0;
       }
   }
 }
 
 void initializeData(float val){
   for (int i =0; i < numData; i++){
    upper_integrator[i] = new Integrator(val, 30);
    lower_integrator[i] = new Integrator(val, 30);
    data[i] = val;
    upper_offset[i] = 0;
    lower_offset[i] = 0;
    offset[i] = 0;
   }
   
   totalWeight = 0;
   sd = 0;
 }
 
 void initializeUpper(int pos, float val ){
    //println(val);
    upper_integrator[pos] = new Integrator(val, 30);
 }
 
 void initializeLower(int pos, float val ){
    lower_integrator[pos] = new Integrator(val, 30);
 }
 
 
 // Set Functions
 void targetColor(color _color){
   color_integrator.target(_color);
 }
 
 void targetStrokeColor(color _color){
   stroke_color_integrator.target(_color);
 }
 
 void targetHighlightColor(color _color){
   highlight_color_integrator.target(_color);
 }
  
 void targetUpperData(int i, float _data){
   upper_integrator[i].target(_data);
 }
 
 void targetLowerData(int i, float _data){
   lower_integrator[i].target(_data);
 }
 
 void setColor(color _color){
   layerColor = _color;
   targetColor(_color);
   setStrokeColor(_color);
   setHighlightColor(_color);
 }
 
 void setStrokeColor(color _color){
   strokeColor = _color;
   targetStrokeColor(_color);
 }
 
 void setHighlightColor(color _color){
   highlightColor = _color;
   targetHighlightColor(_color);
 }
  
 void setData(int i, float _data){
   data[i] = _data;
   //targetData(i, _data);
 }
 
 void setUpperOffset(int i, float _offset){
   upper_offset[i] = _offset;
   updateOffset(i);
 }
 
 void setLowerOffset(int i, float _offset){
   lower_offset[i] = _offset;
   updateOffset(i);
 }


 // Get Functions
 
 color getColor(){
   return color_integrator.value();
 }
 
 color getStrokeColor(){
   return stroke_color_integrator.value();
 }
 
 color getHighlightColor(){
   return highlight_color_integrator.value();
 }
 
 float getUpperData(int i){
   return upper_integrator[i].value();
 }
 
 float getLowerData(int i){
   return lower_integrator[i].value();
 }
 
 color getRealColor(){
   return layerColor;
 }
 
 color getRealStrokeColor(){
   return strokeColor;
 }
 
 color getRealHighlightColor(){
   return highlightColor;
 }
 
 float getRealData(int i){
   return data[i];
 }
 
 float getUpperOffset(int i){
   return upper_offset[i];
 }
 
 float getLowerOffset(int i){
   return lower_offset[i];
 }
 
 float getOffset(int i){
   return offset[i];
 }
 
 Float getWeight(){
   calculateWeight();
   return totalWeight;
 }
 
 Float getStandardDev(){
   calculateWeight();
   return sd;
 }
 
 Integer getOnsetTime(){
   for(int i= 0; i< numData; i++){
     if (data[i] != 0) return i;
   }
   
   return numData;
 }
 
 // update functions
 void updateColor(){
   color_integrator.update();
 }
 
 void updateStrokeColor(){
   stroke_color_integrator.update();
 }
 
 void updateHighlightColor(){
   highlight_color_integrator.update();
 }
 
 void updateData(int i){
   upper_integrator[i].update();
   lower_integrator[i].update();
 }
 
 void updateOffset(int i){
   offset[i] = upper_offset[i] - lower_offset[i];
 }
 
 
 void calculateWeight(){
   totalWeight = 0;
   sd = 0;
   float tol = 5;
   for(int i=0; i < numData; i++){
     totalWeight += data[i];
   }
   
   for(int i=1; i < numData; i++){
     if ( abs(data[i] - data[i-1]) > tol)
       sd += abs(data[i] - data[i-1]);
   }
 }
 
 
 void calculateSd(){
   totalWeight = 0;
   sd = 0;
   float tol = 5;
   for(int i=0; i < numData; i++){
     totalWeight += data[i];
   }
   
   for(int i=1; i < numData; i++){
     if ( abs(data[i] - data[i-1]) > tol)
       sd += abs(data[i] - data[i-1]);
   }
 }

}
